package analyticsServer;


import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.rmi.NoSuchObjectException;
import java.rmi.RemoteException;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.Calendar;
import java.util.Vector;

import java.util.regex.Pattern;
import java.util.regex.Matcher;

import analyticsServer.dataLayer.DataLayer;
import analyticsServer.dataLayer.Subscription;

import common.RegistryManagement;
import common.rmi.*;
import common.rmi.event.*;

public class AnalyticsServer extends UnicastRemoteObject implements RemoteAnalyzer {
	
	private static final long serialVersionUID = 1L;
	private static String AnalyticsServerBindingName;
	protected AnalyticsServer() throws RemoteException {
		super(0);
	}


	public static void main(String[] args){
		DataLayer.setKeys();
		AnalyticsServerBindingName=args[0].trim();
		BufferedReader reader= new BufferedReader(new InputStreamReader(System.in));
		try {
			Registry registry = RegistryManagement.createIfNotExists();
			if (registry != null)
			{	AnalyticsServer server=new AnalyticsServer();
				registry.rebind(AnalyticsServerBindingName,server);
				System.out.println("AnalyticsServer is bound");
				while(true){
					if(reader.readLine().trim().equals("!exit")) {
						System.out.println(exit(server));
						break;
					}
				}
			}
	    } catch (Exception e) {
	    	System.err.println("AnalyticsServer exception:");
	        e.printStackTrace();
	    }
	}
	private static String exit(AnalyticsServer server){
		try {
			UnicastRemoteObject.unexportObject(server, true);
		} catch (NoSuchObjectException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return "Succesfully unexported the AnalyticsServer";
	}
	@Override
	public String subscribe(String filter,ClientReference client){
		
		filter=filter.trim();
		Vector<String> eventTypes=new Vector<String>();
		Pattern pattern=Pattern.compile(filter);
		for(int i=0;i<DataLayer.getEventTypes().length;i++){
			Matcher matcher=pattern.matcher(DataLayer.getEventTypes()[i]);
			if(matcher.find()) eventTypes.add(matcher.group());
		}

		
		Subscription subscription=new Subscription(eventTypes,client);
		
			subscription.activate();
		
		return subscription.getID();
	}

	@Override
	public void processEvent(RemoteEvent event) throws RemoteException {
		doStatistics(event);
		Vector<ClientReference> subscriptions=DataLayer.getSubscriptions(event.getType());
		for(ClientReference client: subscriptions){
			try {
				Calendar time=Calendar.getInstance();
				time.setTimeInMillis(event.getTimestamp());
				client.forwardEvent(event.stringRep());
				
			} catch (RemoteException e) {
				System.out.println("ERROR!: Connection with the client can not be established ");
				DataLayer.removeSubscriptionsOf(client);
			}
		}
	}

	@Override
	public String unsubscribe(String subscriptionID) {
		Subscription subscription=DataLayer.getSubscription(subscriptionID);
		subscription.deactivate();
		return "subscription "+subscriptionID+" terminated";
	}
	
	private void doStatistics(RemoteEvent event){
		String eventType;
		try {
			eventType = event.getType();
	
			
			if(eventType.equals("AUCTION_STARTED")){
				if(event instanceof RemoteAuctionEvent){
					DataLayer.auctionBegins(((RemoteAuctionEvent)event).getAuctionID(), event.getTimestamp());
				}	
			}else if(eventType.equals("AUCTION_ENDED")){
				if(event instanceof RemoteAuctionEvent){
					DataLayer.auctionEnds(((RemoteAuctionEvent)event).getAuctionID(), event.getTimestamp());
					this.processEvent(Statistics.calculateAverageAuctionTime());
				}
			}else if(eventType.equals("USER_LOGIN")){
				if(event instanceof RemoteUserEvent){
					
					RemoteUserEvent userEvent=(RemoteUserEvent)event;
					DataLayer.userLoggedIn(userEvent.getUserName(), userEvent.getTimestamp());
				}
				
			}else if(eventType.equals("USER_LOGOUT")){
				if(event instanceof RemoteUserEvent){
					
					RemoteUserEvent userEvent=(RemoteUserEvent)event;
					DataLayer.userLoggedOut(userEvent.getUserName(), userEvent.getTimestamp());
					this.processEvent(Statistics.calculateUserSessionTimeMin());
					this.processEvent(Statistics.calculateUserSessionTimeMax());
					this.processEvent(Statistics.calculateUserSessionTimeAverage());
				}
			}else if(eventType.equals("USER_DISCONNECTED")){
				if(event instanceof RemoteUserEvent){
					RemoteUserEvent userEvent=(RemoteUserEvent)event;
					DataLayer.userLoggedOut(userEvent.getUserName(), userEvent.getTimestamp());
					this.processEvent(Statistics.calculateUserSessionTimeMin());
					this.processEvent(Statistics.calculateUserSessionTimeMax());
					this.processEvent(Statistics.calculateUserSessionTimeAverage());
				}
				
			}else if(eventType.equals("BID_PLACED")){
				if(event instanceof RemoteBidEvent){
					RemoteBidEvent bidEvent=(RemoteBidEvent) event;
					DataLayer.bidPlaced(bidEvent.getPrice(), bidEvent.getTimestamp());
					this.processEvent(Statistics.calculateMaxBidPrice());
					this.processEvent(Statistics.calculateBidCountPerMinute());
				}
			}else if(eventType.equals("BID_WON")){
					DataLayer.increaseWonBidCount();
					this.processEvent(Statistics.calculateSuccesfulAuctionRatio());
			}
		} catch (RemoteException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
